// Copyright 2012 Jetro Lauha - http://iki.fi/jetro/

#ifdef _WIN32
#include <Windows.h>
#endif
#include "GL/GL.h"

#include <irrlicht.h>
#include <cstring>
#include <cassert>
#include <cstdio>
#include "demo.h"
#include "fx_puffpts.h"


#define VTX_COLOR   0xffffffff
#define CENTER_X    0
#define CENTER_Y    0
#define SCALE_X     0.003f
#define SCALE_Y     -0.003f


using namespace irr;

using namespace std;


class PuffPtsShaderCallBack : public video::IShaderConstantSetCallBack
{
public:
	float mTime;
    video::SColorf mColor;

    virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
    {
        video::IVideoDriver *driver = services->getVideoDriver();

        core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
        invWorld.makeInverse();

        services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);

        core::matrix4 worldViewProj;
        worldViewProj = driver->getTransform(video::ETS_PROJECTION);
        worldViewProj *= driver->getTransform(video::ETS_VIEW);
        worldViewProj *= driver->getTransform(video::ETS_WORLD);

        services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);

        core::matrix4 world = driver->getTransform(video::ETS_WORLD);
        world = world.getTransposed();

        services->setVertexShaderConstant("mTransWorld", world.pointer(), 16);

		services->setVertexShaderConstant("mTime", reinterpret_cast<f32*>(&mTime), 1);
        const float cf[4] = { mColor.r, mColor.g, mColor.b, mColor.a };
        services->setVertexShaderConstant("mColor", cf, 4);

        s32 textureLayer = 0;
        services->setPixelShaderConstant("texture", &textureLayer, 1);
    }
};


static scene::ISceneManager *sSceneManager = 0;
static scene::IMeshSceneNode *sLogo = 0;

static PuffPtsShaderCallBack sPuffPtsShaderCallBack;


bool PuffPtsFx::init()
{
    scene::SMeshBuffer *mb = new scene::SMeshBuffer;
    video::S3DVertex vtx;
    vtx.Normal.set(0, 0, 0);
    vtx.TCoords.set(0, 0);
    vtx.Color.set(VTX_COLOR);
    //mRotationSpeed = 1.0f;

    float screenW = (float)gIrrDevice->getVideoDriver()->getScreenSize().Width;
    float screenH = (float)gIrrDevice->getVideoDriver()->getScreenSize().Height;
    float rangescale = 2.0f;
    const float pscale = 0.01f;
    const float cx = pscale * screenH / screenW;
    const float cy = pscale;

    for (int a = 0; a < 16384; ++a)
    {
        float x = rangescale * (2.0f * (rand() / (float)RAND_MAX) - 1.0f);
        float y = rangescale * (2.0f * (rand() / (float)RAND_MAX) - 1.0f);
        float z = rangescale * (2.0f * (rand() / (float)RAND_MAX) - 1.0f);
        float r = rand() / (float)RAND_MAX;
        vtx.Pos.set(x, y, z);
        vtx.Normal.set(-cx, -cy, r);
        vtx.TCoords.set(0, 0);
        mb->Vertices.push_back(vtx);
        vtx.Normal.set(cx, -cy, r);
        vtx.TCoords.set(1, 0);
        mb->Vertices.push_back(vtx);
        vtx.Normal.set(cx, cy, r);
        vtx.TCoords.set(1, 1);
        mb->Vertices.push_back(vtx);
        vtx.Normal.set(-cx, cy, r);
        vtx.TCoords.set(0, 1);
        mb->Vertices.push_back(vtx);
        mb->Indices.push_back((u16)(a * 4));
        mb->Indices.push_back((u16)(a * 4 + 1));
        mb->Indices.push_back((u16)(a * 4 + 2));
        mb->Indices.push_back((u16)(a * 4));
        mb->Indices.push_back((u16)(a * 4 + 2));
        mb->Indices.push_back((u16)(a * 4 + 3));
    }

    //mb->getMaterial().setFlag(video::EMF_POINTCLOUD, true);
    mb->recalculateBoundingBox();

    mb->setHardwareMappingHint(scene::EHM_STATIC);
    scene::SMesh *mesh = new scene::SMesh;
    mesh->addMeshBuffer(mb);
    mesh->recalculateBoundingBox();
    mb->drop();

    core::vector3df cameraPosition(0, 0.5f, -1.5f);

    sSceneManager = gIrrDevice->getSceneManager()->createNewSceneManager();
    sSceneManager->addCameraSceneNode();
    sSceneManager->getActiveCamera()->setPosition(cameraPosition);

    //sLogo = sSceneManager->addCubeSceneNode(1);
    sLogo = sSceneManager->addMeshSceneNode(mesh);
    mesh->drop();

	s32 materialType;

    video::ITexture *texture = gIrrDevice->getVideoDriver()->getTexture("data/oldies/kuksapuff.png");

    video::IGPUProgrammingServices *gpups = gIrrDevice->getVideoDriver()->getGPUProgrammingServices();
	materialType = gpups->addHighLevelShaderMaterialFromFiles("data/fx_puffpts.vs", "data/fx_puffpts.fs", &sPuffPtsShaderCallBack, video::EMT_TRANSPARENT_ADD_COLOR, 0);

    video::SMaterial &mat = sLogo->getMaterial(0);
    mat.BlendOperation = video::EBO_ADD;
    mat.setFlag(video::EMF_LIGHTING, false);
    //mat.setFlag(video::EMF_POINTCLOUD, true);
    mat.setFlag(video::EMF_BACK_FACE_CULLING, false);
    mat.setFlag(video::EMF_ZWRITE_ENABLE, false);
    //mat.MaterialType = video::EMT_ONETEXTURE_BLEND;
    //mat.MaterialTypeParam = video::pack_textureBlendFunc(video::EBF_ONE, video::EBF_ONE, video::EMFN_MODULATE_1X, video::EAS_NONE);
    //mat.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
	mat.MaterialType = (video::E_MATERIAL_TYPE)materialType;
    mat.Thickness = 8;
    mat.setTexture(0, texture);

    mInitialized = true;
    return true;
}


void PuffPtsFx::render(double time, int timeMs)
{
    static double prevTime = 0;
    float deltaTime = (float)(time - prevTime);

    if (!sLogo || !sSceneManager)
        return;

	sPuffPtsShaderCallBack.mTime = (float)time;
    //sPuffPtsShaderCallBack.mColor = video::SColorf(0.3f, 0.3f, 0.3f, 1.0f);
    sPuffPtsShaderCallBack.mColor = mColor;

    float xRotationSpeed = (1.0f - sBoxStep((float)time, 0, 180.0f)) * 20 - 10;
    float yRotationSpeed = sBoxStep((float)time, 0, 180.0f) * 20 - 10;

    static core::vector3df eulerAngles(0, 0, 0);
    eulerAngles.X += deltaTime * xRotationSpeed;
    eulerAngles.Y += deltaTime * yRotationSpeed;
    sLogo->setRotation(eulerAngles);
    sSceneManager->drawAll();
	float nofxSecs = 2.0f;
    float amount = core::clamp<float>((float)(((time - mFxStartTime - nofxSecs) / 5.0)), 0, 1);

    prevTime = time;
}


void PuffPtsFx::deinit()
{
    if (sSceneManager)
        sSceneManager->drop();
    sSceneManager = 0;
    mInitialized = false;
}
